libext: fix heap overflow in ext_readlink and ext_readdir from a crafted ext4 image - #1449
libext: fix heap overflow in ext_readlink and ext_readdir from a crafted ext4 image#1449gburd wants to merge 1 commit into
Conversation
|
Related upstream note: while auditing the ext/ext4 image-trust surface I also found an out-of-bounds read in the vendored lwext4 ( Since |
13fe54a to
4d81010
Compare
… from a crafted ext4 image
Two memory-safety bugs reachable by mounting an attacker-supplied ext4 image
(a realistic threat for attached volumes / multi-tenant hosts). In a unikernel
these are kernel heap corruption.
1. ext_readlink() heap buffer overflow (critical). For a slow symlink it did:
void *buf = malloc(block_size); // e.g. 4096 bytes
ext_internal_read(fs, ref, offset, buf, fsize, &read_count);
where fsize is ext4_inode_get_size() - the raw 64-bit inode size read
straight off disk, fully attacker-controlled and NOT clamped. A crafted
symlink inode with i_size = 1 MiB (and i_blocks != 0 so the slow-symlink
branch is taken) makes ext_internal_read write ~1 MiB into the 4 KiB heap
buffer -> heap corruption / potential RCE, reached by readlink() or any path
traversal through the link. A symlink target is at most one block (bounded
by PATH_MAX), so clamp: reject fsize > block_size, guard uio_offset >= fsize
(which would otherwise underflow fsize-offset), and check malloc().
2. ext_readdir() d_name overflow. memcpy(dir->d_name, name, name_length) with
name_length from ext4_dir_en_get_name_len(), which on an old-rev image
(rev0, minor<5) folds in name_length_high and returns up to block_size-8
(~4088) while d_name is 256 bytes. Clamp name_length to sizeof(d_name)-1.
Verified with debugfs-crafted images: a normal symlink still reads back
correctly (tst-ext-readlink), and a symlink inode with i_size=1MiB now returns
EINVAL instead of overflowing the heap. Added tst-ext-readlink as a regression.
4d81010 to
63444df
Compare
Security fix — Critical: kernel heap overflow from mounting a crafted ext4 image (a realistic threat for attached volumes / multi-tenant hosts; in a unikernel this is kernel heap corruption → potential RCE).
1.
ext_readlink()heap buffer overflow (Critical)For a slow symlink,
modules/libext/ext_vnops.ccdid:fsize = ext4_inode_get_size()is the raw 64-bit inode size read straight off disk — fully attacker-controlled and not clamped toblock_size.ext_internal_readthen writes ~fsizebytes into theblock_sizeheap buffer.PoC (verified with debugfs): craft an ext4 image with a symlink inode whose
i_size = 0x100000(1 MiB) andi_blocks != 0(so the slow-symlinkelsebranch is taken).readlink()— or any path traversal through the link — →malloc(4096)then a ~1 MiB write into it → heap corruption. Alsouio_offset > fsizeunderflowsfsize - offsetinsideext_internal_read.Fix: a symlink target is at most one block (bounded by PATH_MAX); reject
fsize > block_size, guarduio_offset >= fsize, checkmalloc.2.
ext_readdir()d_name[256]overflow (High, old-rev image)On a rev-0 (minor<5) image,
ext4_dir_en_get_name_lenfolds inname_length_highand can return up toblock_size-8(~4088) → ~4 KB memcpy into the 256-byted_name. Fix: clamp tosizeof(d_name)-1.Verification
With debugfs-crafted images: a normal symlink still reads back correctly (new
tst-ext-readlinkregression), and a symlink inode withi_size=1MiBnow returnsEINVALinstead of overflowing the heap.Severity
#1 Critical (CVSS ~8.4, AV:L/needs mounted crafted image/C:H/I:H/A:H — RCE-class). #2 High.
Both bugs are in shipped
master(the OSv ext/lwext4 glue), not new code.